#include  "gtest/gtest.h"

#include "infra_queue.h"

TEST(queueTest, queueCreateDestroy)
{
    int32_t ret = 0;
    int32_t queue_num = 50;
    infra_queue_handle handle = NULL;

    handle = infra_queue_create(queue_num, queue_num, true, "test");
    ASSERT_TRUE(handle != NULL);

    ret = infra_queue_destroy(handle);
    EXPECT_EQ(ret, 0);
}

TEST(queueTest, queuePushPop0)
{
    int32_t ret = 0, idx = 0, queue_num = 50;
    int32_t *pitem = (int32_t *)NULL;
    void *p[queue_num] = {NULL};
    void *data = NULL;
    infra_queue_handle handle = NULL;

    for (idx = 0; idx < queue_num; idx++) {
        p[idx] = malloc(sizeof(int32_t) * 4);
        *(int32_t *)(p[idx]) = idx;
        pitem = (int32_t *)p[idx];
    }

    handle = infra_queue_create(queue_num, queue_num, true, "test");
    ASSERT_TRUE(handle != NULL);
    for (idx = 0; idx < queue_num; idx++) {
        ret = infra_queue_push(handle, p[idx]);
    }

    for (idx = 0; idx < queue_num; idx++) {
        pitem = (int32_t *)infra_queue_pop(handle, -1);
        ASSERT_TRUE(pitem != NULL);
        EXPECT_EQ(*pitem, *(int32_t *)(p[idx]));
    }

    ret = infra_queue_destroy(handle);
    EXPECT_EQ(ret, 0);

    for (idx = 0; idx < queue_num; idx++) {
        free(p[idx]);
    }
}

TEST(queueTest, queuePushPop1)
{
    int32_t ret = 0, idx = 0, queue_num = 50;
    int32_t *pitem = (int32_t *)NULL;
    void *p[queue_num + 1] = {NULL};
    void *data = NULL;
    infra_queue_handle handle = NULL;

    for (idx = 0; idx < queue_num + 1; idx++) {
        p[idx] = malloc(sizeof(int32_t) * 4);
        *(int32_t *)(p[idx]) = idx;
        pitem = (int32_t *)p[idx];
    }

    handle = infra_queue_create(queue_num, queue_num, true, "test");
    ASSERT_TRUE(handle != NULL);
    for (idx = 0; idx < queue_num; idx++) {
        ret = infra_queue_push(handle, p[idx]);
    }

    ret = infra_queue_push(handle, p[50]);
    EXPECT_EQ(ret, 0);

    for (idx = 0; idx < queue_num; idx++) {
        pitem = (int32_t *)infra_queue_pop(handle, -1);
        ASSERT_TRUE(pitem != NULL);
        EXPECT_EQ(*pitem, *(int32_t *)(p[idx + 1]));
    }

    pitem = (int32_t *)infra_queue_pop(handle, -1);
    EXPECT_TRUE(pitem == NULL);

    ret = infra_queue_destroy(handle);
    EXPECT_EQ(ret, 0);

    for (idx = 0; idx < queue_num; idx++) {
        free(p[idx]);
    }
}

TEST(queueTest, queuePushPop2)
{
    int32_t ret = 0, idx = 0, queue_num = 50;
    int32_t *pitem = (int32_t *)NULL;
    void *p[queue_num] = {NULL};
    void *data = NULL;
    infra_queue_handle handle = NULL;

    for (idx = 0; idx < queue_num; idx++) {
        p[idx] = malloc(sizeof(int32_t) * 4);
        *(int32_t *)(p[idx]) = idx;
        pitem = (int32_t *)p[idx];
    }

    handle = infra_queue_create(queue_num, queue_num, true, "test");
    ASSERT_TRUE(handle != NULL);

    pitem = (int32_t *)infra_queue_pop(handle, 30);
    EXPECT_TRUE(pitem == NULL);

    for (idx = 0; idx < queue_num; idx++) {
        ret = infra_queue_push(handle, p[idx]);
    }

    for (idx = 0; idx < queue_num; idx++) {
        pitem = (int32_t *)infra_queue_pop(handle, 30);
        ASSERT_TRUE(pitem != NULL);
        EXPECT_EQ(*pitem, *(int32_t *)(p[idx]));
    }

    ret = infra_queue_destroy(handle);
    EXPECT_EQ(ret, 0);

    for (idx = 0; idx < queue_num; idx++) {
        free(p[idx]);
    }
}

TEST(queueTest, queueCleanup)
{
    int32_t ret = 0, idx = 0, queue_num = 50;
    int32_t *pitem = (int32_t *)NULL;
    void *p[queue_num] = {NULL};
    void *data = NULL;
    infra_queue_handle handle = NULL;

    for (idx = 0; idx < queue_num; idx++) {
        p[idx] = malloc(sizeof(int32_t) * 4);
        *(int32_t *)(p[idx]) = idx;
        pitem = (int32_t *)p[idx];
    }

    handle = infra_queue_create(queue_num, queue_num, true, "test");
    ASSERT_TRUE(handle != NULL);

    for (idx = 0; idx < queue_num; idx++) {
        ret = infra_queue_push(handle, p[idx]);
    }

    for (idx = 0; idx < queue_num - 10; idx++) {
        pitem = (int32_t *)infra_queue_pop(handle, -1);
        ASSERT_TRUE(pitem != NULL);
    }

    ret = infra_queue_cleanup(handle);
    EXPECT_EQ(ret, 0);

    ret = infra_queue_destroy(handle);
    EXPECT_EQ(ret, 0);

    for (idx = 0; idx < queue_num; idx++) {
        free(p[idx]);
    }
}

TEST(queueTest, queueSetDepth0)
{
    int32_t ret = 0, idx = 0, queue_num = 50;
    int32_t *pitem = (int32_t *)NULL;
    void *p[queue_num] = {NULL};
    void *data = NULL;
    infra_queue_handle handle = NULL;

    for (idx = 0; idx < queue_num; idx++) {
        p[idx] = malloc(sizeof(int32_t) * 4);
        *(int32_t *)(p[idx]) = idx;
        pitem = (int32_t *)p[idx];
    }

    handle = infra_queue_create(5, queue_num, true, "test");
    ASSERT_TRUE(handle != NULL);

    for (idx = 0; idx < 10; idx++) {
        ret = infra_queue_push(handle, p[idx]);
        EXPECT_EQ(0, ret);
    }

    for (idx = 0; idx < 5; idx++) {
        pitem = (int32_t *)infra_queue_pop(handle, -1);
        ASSERT_TRUE(pitem != NULL);
        EXPECT_EQ(*pitem, *(int32_t *)(p[idx + 5]));
    }

    ret = infra_queue_destroy(handle);
    EXPECT_EQ(ret, 0);

    for (idx = 0; idx < queue_num; idx++) {
        free(p[idx]);
    }
}

TEST(queueTest, queueSetDepth1)
{
    int32_t ret = 0, idx = 0, queue_num = 50;
    int32_t *pitem = (int32_t *)NULL;
    void *p[queue_num] = {NULL};
    void *data = NULL;
    infra_queue_handle handle = NULL;

    for (idx = 0; idx < queue_num; idx++) {
        p[idx] = malloc(sizeof(int32_t) * 4);
        *(int32_t *)(p[idx]) = idx;
        pitem = (int32_t *)p[idx];
    }

    handle = infra_queue_create(10, queue_num, true, "test");
    ASSERT_TRUE(handle != NULL);

    for (idx = 0; idx < 10; idx++) {
        ret = infra_queue_push(handle, p[idx]);
        EXPECT_EQ(0, ret);
    }

    ret = infra_queue_set_depth(handle, 5);
    EXPECT_EQ(0, ret);

    for (idx = 0; idx < 5; idx++) {
        ret = infra_queue_push(handle, p[idx + 10]);
        EXPECT_EQ(0, ret);
    }

    for (idx = 0; idx < 5; idx++) {
        pitem = (int32_t *)infra_queue_pop(handle, -1);
        ASSERT_TRUE(pitem != NULL);
        EXPECT_EQ(*pitem, *(int32_t *)(p[idx + 10]));
    }

    ret = infra_queue_destroy(handle);
    EXPECT_EQ(ret, 0);

    for (idx = 0; idx < queue_num; idx++) {
        free(p[idx]);
    }
}

int32_t queue_giveback_callback(void *puserdata, void *pdata)
{
    int32_t *pitem = (int32_t *)NULL;

    pitem = (int32_t *)pdata;
    *pitem = *pitem + 5;
    // printf("pitem:%d\n", *pitem);

    return 0;
}

TEST(queueTest, queueGivebackCb0)
{
    int32_t ret = 0, idx = 0, queue_num = 50;
    int32_t *pitem = (int32_t *)NULL;
    void *p[queue_num] = {NULL};
    void *data = NULL;
    infra_queue_handle handle = NULL;

    for (idx = 0; idx < queue_num; idx++) {
        p[idx] = malloc(sizeof(int32_t) * 4);
        *(int32_t *)(p[idx]) = idx;
        pitem = (int32_t *)p[idx];
    }

    handle = infra_queue_create(5, queue_num, true, "test");
    ASSERT_TRUE(handle != NULL);

    ret = infra_queue_register_giveback_callback(handle, queue_giveback_callback, p);
    EXPECT_EQ(0, ret);

    for (idx = 0; idx < 10; idx++) {
        ret = infra_queue_push(handle, p[idx]);
        EXPECT_EQ(0, ret);
    }

    for (idx = 0; idx < 5; idx++) {
        pitem = (int32_t *)infra_queue_pop(handle, -1);
        ASSERT_TRUE(pitem != NULL);
        EXPECT_EQ(*pitem, *(int32_t *)(p[idx + 5]));
    }

    for (idx = 0; idx < 5; idx++) {
        pitem = (int32_t *)p[idx];
        EXPECT_EQ(*pitem, 5 + idx);
    }

    ret = infra_queue_destroy(handle);
    EXPECT_EQ(ret, 0);

    for (idx = 0; idx < queue_num; idx++) {
        free(p[idx]);
    }
}

int32_t queue_notify_callback(void *puserdata, void *pdata)
{
    int32_t *pnt_num = (int32_t *)puserdata;

    *pnt_num = (*pnt_num) + 1;
    // printf("pnt_num:%p, nt_num:%d\n", pnt_num, *pnt_num);

    return 0;
}

TEST(queueTest, queueNotifyCb0)
{
    int32_t ret = 0, idx = 0, queue_num = 50;
    int32_t nt_num = 0;
    int32_t *pitem = (int32_t *)NULL;
    void *p[queue_num] = {NULL};
    infra_queue_handle handle = NULL;

    for (idx = 0; idx < queue_num; idx++) {
        p[idx] = malloc(sizeof(int32_t) * 4);
        *(int32_t *)(p[idx]) = idx;
        pitem = (int32_t *)p[idx];
    }

    handle = infra_queue_create(5, queue_num, true, "test");
    ASSERT_TRUE(handle != NULL);

    ret = infra_queue_register_notify_callback(handle, queue_notify_callback, &nt_num);
    EXPECT_EQ(0, ret);

    for (idx = 0; idx < 5; idx++) {
        ret = infra_queue_push(handle, p[idx]);
        EXPECT_EQ(0, ret);
    }

    for (idx = 0; idx < 5; idx++) {
        pitem = (int32_t *)infra_queue_pop(handle, -1);
        ASSERT_TRUE(pitem != NULL);
        EXPECT_EQ(*pitem, *(int32_t *)(p[idx]));
    }

    EXPECT_EQ(nt_num, 5);

    ret = infra_queue_destroy(handle);
    EXPECT_EQ(ret, 0);

    for (idx = 0; idx < queue_num; idx++) {
        free(p[idx]);
    }
}

void queue_travel_callback(void *puserdata, void *pdata)
{
    int32_t *pitem = (int32_t *)pdata;
    *pitem = *pitem + 5;
    // printf("pitem:%d\n", *pitem);

    return;
}

TEST(queueTest, queueTravel)
{
    int32_t ret = 0, idx = 0, queue_num = 50;
    int32_t *pitem = (int32_t *)NULL;
    void *p[queue_num] = {NULL};
    void *data = NULL;
    infra_queue_handle handle = NULL;

    for (idx = 0; idx < queue_num; idx++) {
        p[idx] = malloc(sizeof(int32_t) * 4);
        *(int32_t *)(p[idx]) = idx;
        pitem = (int32_t *)p[idx];
    }

    handle = infra_queue_create(queue_num, queue_num, true, "test");
    ASSERT_TRUE(handle != NULL);
    for (idx = 0; idx < queue_num; idx++) {
        ret = infra_queue_push(handle, p[idx]);
    }

    infra_queue_travel(handle, queue_travel_callback, p);

    for (idx = 0; idx < queue_num; idx++) {
        pitem = (int32_t *)infra_queue_pop(handle, -1);
        ASSERT_TRUE(pitem != NULL);
        EXPECT_EQ(*pitem, *(int32_t *)(p[idx]));
    }

    ret = infra_queue_destroy(handle);
    EXPECT_EQ(ret, 0);

    for (idx = 0; idx < queue_num; idx++) {
        free(p[idx]);
    }
}

bool queue_find_callback(void *puserdata, void *pdata)
{
    int32_t *pitem = (int32_t *)pdata;

    if (*pitem == 34) {
        return true;
    }

    return false;
}

TEST(queueTest, queueFind)
{
    int32_t ret = 0, idx = 0, queue_num = 50;
    int32_t *pitem = (int32_t *)NULL;
    void *p[queue_num] = {NULL};
    void *data = NULL;
    infra_queue_handle handle = NULL;

    for (idx = 0; idx < queue_num; idx++) {
        p[idx] = malloc(sizeof(int32_t) * 4);
        *(int32_t *)(p[idx]) = idx;
        pitem = (int32_t *)p[idx];
    }

    handle = infra_queue_create(queue_num, queue_num, true, "test");
    ASSERT_TRUE(handle != NULL);
    for (idx = 0; idx < queue_num; idx++) {
        ret = infra_queue_push(handle, p[idx]);
    }

    pitem = (int32_t *)infra_queue_find(handle, queue_find_callback, p);
    ASSERT_TRUE((pitem != NULL));
    EXPECT_EQ(*pitem, 34);

    for (idx = 0; idx < queue_num; idx++) {
        pitem = (int32_t *)infra_queue_pop(handle, -1);
        ASSERT_TRUE(pitem != NULL);
        EXPECT_EQ(*pitem, *(int32_t *)(p[idx]));
    }

    ret = infra_queue_destroy(handle);
    EXPECT_EQ(ret, 0);

    for (idx = 0; idx < queue_num; idx++) {
        free(p[idx]);
    }
}

TEST(queueTest, queueFront)
{
    int32_t ret = 0, idx = 0, queue_num = 50;
    int32_t *pitem = (int32_t *)NULL;
    void *p[queue_num] = {NULL};
    void *data = NULL;
    infra_queue_handle handle = NULL;

    for (idx = 0; idx < queue_num; idx++) {
        p[idx] = malloc(sizeof(int32_t) * 4);
        *(int32_t *)(p[idx]) = idx;
        pitem = (int32_t *)p[idx];
    }

    handle = infra_queue_create(queue_num, queue_num, true, "test");
    ASSERT_TRUE((handle != NULL));
    for (idx = 0; idx < queue_num; idx++) {
        ret = infra_queue_push(handle, p[idx]);
    }

    pitem = (int32_t *)infra_queue_front(handle);
    ASSERT_TRUE((pitem != NULL));
    EXPECT_EQ(*pitem, 0);

    for (idx = 0; idx < queue_num; idx++) {
        pitem = (int32_t *)infra_queue_pop(handle, -1);
        ASSERT_TRUE(pitem != NULL);
        EXPECT_EQ(*pitem, *(int32_t *)(p[idx]));
    }

    ret = infra_queue_destroy(handle);
    EXPECT_EQ(ret, 0);

    for (idx = 0; idx < queue_num; idx++) {
        free(p[idx]);
    }
}

TEST(queueTest, queueBack)
{
    int32_t ret = 0, idx = 0, queue_num = 50;
    int32_t *pitem = (int32_t *)NULL;
    void *p[queue_num] = {NULL};
    void *data = NULL;
    infra_queue_handle handle = NULL;

    for (idx = 0; idx < queue_num; idx++) {
        p[idx] = malloc(sizeof(int32_t) * 4);
        *(int32_t *)(p[idx]) = idx;
        pitem = (int32_t *)p[idx];
    }

    handle = infra_queue_create(queue_num, queue_num, true, "test");
    ASSERT_TRUE((handle != NULL));
    for (idx = 0; idx < queue_num; idx++) {
        ret = infra_queue_push(handle, p[idx]);
    }

    pitem = (int32_t *)infra_queue_back(handle);
    ASSERT_TRUE((pitem != NULL));
    EXPECT_EQ(*pitem, 49);

    for (idx = 0; idx < queue_num; idx++) {
        pitem = (int32_t *)infra_queue_pop(handle, -1);
        ASSERT_TRUE(pitem != NULL);
        EXPECT_EQ(*pitem, *(int32_t *)(p[idx]));
    }

    ret = infra_queue_destroy(handle);
    EXPECT_EQ(ret, 0);

    for (idx = 0; idx < queue_num; idx++) {
        free(p[idx]);
    }
}

TEST(queueTest, queueEmpty)
{
    int32_t ret = 0, idx = 0, queue_num = 50;
    int32_t *pitem = (int32_t *)NULL;
    void *p[queue_num] = {NULL};
    void *data = NULL;
    infra_queue_handle handle = NULL;

    for (idx = 0; idx < queue_num; idx++) {
        p[idx] = malloc(sizeof(int32_t) * 4);
        *(int32_t *)(p[idx]) = idx;
        pitem = (int32_t *)p[idx];
    }

    handle = infra_queue_create(queue_num, queue_num, true, "test");
    ASSERT_TRUE(handle != NULL);
    for (idx = 0; idx < queue_num; idx++) {
        ret = infra_queue_push(handle, p[idx]);
    }

    for (idx = 0; idx < queue_num; idx++) {
        pitem = (int32_t *)infra_queue_pop(handle, -1);
        ASSERT_TRUE(pitem != NULL);
        EXPECT_EQ(*pitem, *(int32_t *)(p[idx]));
    }

    EXPECT_TRUE(infra_queue_empty(handle));

    ret = infra_queue_destroy(handle);
    EXPECT_EQ(ret, 0);

    for (idx = 0; idx < queue_num; idx++) {
        free(p[idx]);
    }

}

TEST(queueTest, queueSize)
{
    int32_t ret = 0, idx = 0, queue_num = 50;
    int32_t *pitem = (int32_t *)NULL;
    void *p[queue_num] = {NULL};
    void *data = NULL;
    infra_queue_handle handle = NULL;

    for (idx = 0; idx < queue_num; idx++) {
        p[idx] = malloc(sizeof(int32_t) * 4);
        *(int32_t *)(p[idx]) = idx;
        pitem = (int32_t *)p[idx];
    }

    handle = infra_queue_create(queue_num, queue_num, true, "test");
    ASSERT_TRUE(handle != NULL);
    for (idx = 0; idx < queue_num; idx++) {
        ret = infra_queue_push(handle, p[idx]);
    }

    EXPECT_TRUE(infra_queue_size(handle) == queue_num);

    for (idx = 0; idx < queue_num; idx++) {
        pitem = (int32_t *)infra_queue_pop(handle, -1);
        ASSERT_TRUE(pitem != NULL);
        EXPECT_EQ(*pitem, *(int32_t *)(p[idx]));
    }

    ret = infra_queue_destroy(handle);
    EXPECT_EQ(ret, 0);

    for (idx = 0; idx < queue_num; idx++) {
        free(p[idx]);
    }

}

TEST(queueTest, queueGetStat)
{
    int32_t ret = 0, idx = 0, queue_num = 50;
    int32_t *pitem = (int32_t *)NULL;
    void *p[queue_num] = {NULL};
    void *data = NULL;
    infra_queue_handle handle = NULL;
    infra_queue_stat_info_t stat_info = {0};

    for (idx = 0; idx < queue_num; idx++) {
        p[idx] = malloc(sizeof(int32_t) * 4);
        *(int32_t *)(p[idx]) = idx;
        pitem = (int32_t *)p[idx];
    }

    handle = infra_queue_create(queue_num, queue_num, true, "test");
    ASSERT_TRUE(handle != NULL);
    for (idx = 0; idx < queue_num; idx++) {
        ret = infra_queue_push(handle, p[idx]);
    }

    EXPECT_EQ(0, infra_queue_get_stat(handle, &stat_info));
    EXPECT_EQ(queue_num, stat_info.in_count);
    EXPECT_EQ(0, stat_info.out_count);
    EXPECT_EQ(queue_num, stat_info.stash_count);

    for (idx = 0; idx < queue_num; idx++) {
        pitem = (int32_t *)infra_queue_pop(handle, -1);
        ASSERT_TRUE(pitem != NULL);
        EXPECT_EQ(*pitem, *(int32_t *)(p[idx]));
    }

    EXPECT_EQ(0, infra_queue_get_stat(handle, &stat_info));
    EXPECT_EQ(queue_num, stat_info.in_count);
    EXPECT_EQ(queue_num, stat_info.out_count);
    EXPECT_EQ(0, stat_info.stash_count);

    ret = infra_queue_destroy(handle);
    EXPECT_EQ(ret, 0);

    for (idx = 0; idx < queue_num; idx++) {
        free(p[idx]);
    }

}
